﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;

namespace LooseChart
{
    /// <summary>
    /// 密度图,单色带透明效果
    /// </summary>
    public class DensitySeries : XYSeries
    {
        private const byte DefaultMinOpacity = 0x10;
        /// <summary>
        /// 最小不透明度,最小为0,密度分级从该值到255
        /// </summary>
        public byte MinOpacity
        {
            get { return (byte)GetValue(MinOpacityProperty); }
            set { SetValue(MinOpacityProperty, value); }
        }
        public static readonly DependencyProperty MinOpacityProperty = DependencyProperty.Register("MinOpacity", typeof(byte), typeof(DensitySeries), new PropertyMetadata(DefaultMinOpacity, (d, e) => { if (d is ISeries series) series.Redraw(); }));

        public DensitySeries() : base() { }

        /// <summary>
        /// 绘制内容
        /// </summary>
        /// <param name="color">基础颜色</param>
        /// <param name="list">矢量数据流</param>
        /// <param name="dc">画布</param>
        protected override void DrawDrawingContext(Color color, List<StreamGeometry> list, DrawingContext dc)
        {
            if (list == null || list.Count <= 0) return;
            var arange = color.A - MinOpacity;
            var pa = arange <= 0 ? 0d : (arange * 1d / list.Count);
            for (int i = 0; i < list.Count; i++)
            {
                var stream = list[i];
                var brush = new SolidColorBrush(Color.FromArgb(Convert.ToByte(Math.Floor(i * pa) + MinOpacity), color.R, color.G, color.B));
                brush.Freeze();
                Brush fill = ShowFill ? brush : null;
                Pen pen = ShowStroke ? new Pen(brush, StrokeThickness) : null;
                if (pen != null) pen.Freeze();
                dc.DrawGeometry(fill, pen, stream);
            }
        }

        /// <summary>
        /// 获取数据源的矢量数据流
        /// </summary>
        /// <param name="chartDataSource">数据源</param>
        /// <returns></returns>
        protected override async Task UpdateStreamGeometry(CancellationToken token)
        {
            if (!this.IsLoaded || token.IsCancellationRequested || XAxis == null || YAxis == null) return;
            var points = Statistics;
            if (points == null || points.Count <= 0)
            {
                MainGeometry = null;
                return;
            }
            var pointLength = PointLength;
            try
            {
                var temp = await Task.Factory.StartNew<List<StreamGeometry>>(() =>
                {
                    var maxcount = points.Max(p => p.Count);
                    var mincount = points.Min(p => p.Count);
                    var range = maxcount - mincount;
                    if (range <= 0 || maxcount <= 0) return null;
                    StreamGeometry[] streams = new StreamGeometry[range + 1];
                    StreamGeometryContext[] contexts = new StreamGeometryContext[range + 1];
                    for (int j = 0; j <= range; j++)
                    {
                        StreamGeometry stream = new StreamGeometry();
                        streams[j] = stream;
                        contexts[j] = stream.Open();
                    }
                    foreach (var point in points)
                    {
                        if (token.IsCancellationRequested) return null;
                        if (point.Count < mincount || point.Count > maxcount) continue;
                        var j = point.Count - mincount;
                        var sgc = contexts[j];
                        var otherpoints = new Point[]
                        {
                            new Point(point.Point.X + pointLength, point.Point.Y),
                            new Point(point.Point.X + pointLength, point.Point.Y + pointLength),
                            new Point(point.Point.X, point.Point.Y + pointLength)
                        };
                        sgc.BeginFigure(point.Point, true, true);
                        sgc.PolyLineTo(otherpoints, true, false);
                    }
                    for (int j = 0; j <= range; j++)
                    {
                        contexts[j].Close();
                        streams[j].Freeze();
                    }
                    return streams.ToList();
                }, token);
                if (!token.IsCancellationRequested) MainGeometry = temp;
            }
            catch (TaskCanceledException e)
            {
                Debug.WriteLine(string.Concat("密度图生成矢量图线程被取消", e));
            }
        }
    }
}
